package com.leesche.netdoll.communication;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.leesche.framework.core.lang.StringUtil;
import com.leesche.netdoll.communication.event.MessageEventDispatcher;
import com.leesche.netdoll.communication.event.SessionChangedEvent;
import com.leesche.netdoll.communication.event.SessionChangedEvent.SessionStatus;

import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import io.netty.util.internal.PlatformDependent;

public final class Session {

	private static final Logger logger = LogManager.getLogger(Session.class);

	public static final AttributeKey<String> KEY_DEVICE_ID = AttributeKey.valueOf("DEVICE_ID");
	public static final AttributeKey<String> KEY_GAME_ID = AttributeKey.valueOf("GAME_ID");

	private volatile static Session instance;

	/** 已连接设备Channel Map */
	private final ConcurrentMap<String, Channel> nonServerChannels = PlatformDependent.newConcurrentHashMap();

	private final ConcurrentMap<String, AtomicInteger> deviceSequeces = PlatformDependent.newConcurrentHashMap();

	private Session() {
	}

	public static Session session() {
		if (instance == null) {
			synchronized (Session.class) {
				if (instance == null)
					instance = new Session();
			}
		}
		return instance;
	}

	public boolean add(Channel channel) {

		if (channel == null || !channel.isActive()) {
			return false;
		}

		String deviceId = channel.attr(KEY_DEVICE_ID).get();

		if (StringUtil.isEmpty(deviceId)) {
			return false;
		}

		nonServerChannels.put(deviceId, channel);
		deviceSequeces.putIfAbsent(deviceId, new AtomicInteger(0));

		MessageEventDispatcher.dispatcher().dispatch(new SessionChangedEvent(deviceId, SessionStatus.Alive));

		return true;
	}

	public boolean remove(Channel channel, String closeBy) {

		if (channel == null)
			return false;

		String deviceId = channel.attr(KEY_DEVICE_ID).get();
		String gameId = channel.attr(KEY_DEVICE_ID).get();

		if (StringUtil.isEmpty(deviceId))
			return false;

		logger.info("remove channel for device {} by {}...1", deviceId, closeBy);

		Channel oldChannel = nonServerChannels.get(deviceId);

		if (oldChannel == null || !oldChannel.id().asLongText().equals(channel.id().asLongText())) {
			return false;
		}

		logger.info("remove channel for device {} by {}...2", deviceId, closeBy);

		Channel removedChannel = nonServerChannels.remove(deviceId);

		if (removedChannel != null) {
			if (removedChannel.id().asLongText().equals(channel.id().asLongText())) {
				logger.info("remove channel for device {} by {}...21", deviceId, closeBy);
				MessageEventDispatcher.dispatcher().dispatch(new SessionChangedEvent(deviceId, gameId, SessionStatus.Dead));
				return true;
			}

			logger.info("remove channel for device {} by {}...22", deviceId, closeBy);
			nonServerChannels.put(deviceId, removedChannel);
			return false;
		}

		logger.info("remove channel for device {} by {}...3", deviceId, closeBy);

		return false;
	}

	public boolean isEmpty() {
		return nonServerChannels.isEmpty();
	}

	public Channel find(String deviceId) {
		return nonServerChannels.get(deviceId);
	}

	public boolean contains(String deviceId) {
		return nonServerChannels.containsKey(deviceId);
	}

	public int getSequece(String deviceId) {
		deviceSequeces.putIfAbsent(deviceId, new AtomicInteger(0));
		AtomicInteger i = deviceSequeces.get(deviceId);
		synchronized (i) {
			i.compareAndSet(65535, 0);
			return i.getAndIncrement();
		}
	}
}
